package cz.data.auth.config;

import com.alibaba.cloud.nacos.NacosDiscoveryProperties;
import com.alibaba.cloud.nacos.discovery.NacosDiscoveryAutoConfiguration;
import com.fasterxml.jackson.module.paramnames.ParameterNamesModule;
import cz.data.common.annotation.DataInner;
import cz.data.common.core.DataConstant;
import cz.data.common.core.DataGrantedAuthority;
import cz.data.common.core.DataRole;
import cz.data.common.core.DataUser;
import cz.data.common.jackson.mixin.DataGrantedAuthorityMixin;
import cz.data.common.jackson.mixin.DataRoleMixin;
import cz.data.common.jackson.mixin.DataUserMixin;
import cz.data.common.support.security.AnnotationMatcherRule;
import cz.data.common.support.security.DynamicRequestMatcherContainer;
import cz.data.common.support.security.DiscoveryServiceHeaderFactory;
import cz.data.common.support.security.DynamicRequestMatcherRule;
import cz.data.common.utils.SecurityModuleUtil;
import cz.data.common.utils.SecurityUtil;
import io.undertow.server.DefaultByteBufferPool;
import io.undertow.websockets.jsr.WebSocketDeploymentInfo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.embedded.undertow.UndertowServletWebServerFactory;
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

@Slf4j
@Configuration
@EnableConfigurationProperties(NacosDiscoveryProperties.class)
@AutoConfigureAfter(NacosDiscoveryAutoConfiguration.class)
public class AppConfig {

    @Bean
    public WebServerFactoryCustomizer<UndertowServletWebServerFactory> webServerFactoryWebServerFactoryCustomizer() {
        return factory ->
                factory.addDeploymentInfoCustomizers(
                        deploymentInfo -> {
                            WebSocketDeploymentInfo webSocketDeploymentInfo = new WebSocketDeploymentInfo();
                            webSocketDeploymentInfo.setBuffers(new DefaultByteBufferPool(false, 1024));
                            deploymentInfo.addServletContextAttribute("io.undertow.websockets.jsr.WebSocketDeploymentInfo", webSocketDeploymentInfo);
                        }
                );
    }

    @Bean
    public Jackson2ObjectMapperBuilderCustomizer defaultJackson2ObjectMapperBuilderCustomizer() {
        return builder ->
                builder.modules(new ParameterNamesModule(),
                                SecurityModuleUtil.simpleModule(),
                                SecurityModuleUtil.javaTimeModule()
                        )
                        .mixIn(DataUser.class, DataUserMixin.class)
                        .mixIn(DataRole.class, DataRoleMixin.class)
                        .mixIn(DataGrantedAuthority.class, DataGrantedAuthorityMixin.class);
    }

    @Bean
    public DynamicRequestMatcherContainer defaultDynamicRequestMatcherContainer(
            RequestMappingHandlerMapping requestMappingHandlerMapping) {
        Map<RequestMappingInfo, HandlerMethod> handlerMethods = requestMappingHandlerMapping.getHandlerMethods();
        Map<RequestMappingInfo, DataInner> dataInnerMap = new HashMap<>();
        for (Map.Entry<RequestMappingInfo, HandlerMethod> entry : handlerMethods.entrySet()) {
            HandlerMethod handlerMethod = entry.getValue();
            Method method = handlerMethod.getMethod();
            DataInner annotation = method.getAnnotation(DataInner.class);
            if (annotation == null)
                continue;
            RequestMappingInfo mappingInfo = entry.getKey();
            dataInnerMap.put(mappingInfo, annotation);
        }
        List<DynamicRequestMatcherRule> matchers = new ArrayList<>();
        for (Map.Entry<RequestMappingInfo, DataInner> entry : dataInnerMap.entrySet()) {
            Set<RequestMethod> requestMethods = entry.getKey().getMethodsCondition().getMethods();
            String[] patternValues = entry.getKey().getPatternValues().toArray(new String[0]);
            for (RequestMethod requestMethod : requestMethods) {
                matchers.add(
                        new AnnotationMatcherRule(
                                HttpMethod.resolve(requestMethod.name()),
                                patternValues, entry.getValue().value())
                );
            }
        }
        return () -> matchers;
    }

    @Bean
    public DiscoveryServiceHeaderFactory nacosServiceHeaderFactory() {
        return new DiscoveryServiceHeaderFactory() {
            private final Map<String, String> headers = new ConcurrentHashMap<>();
            @Override
            public Map<String, String> headers() {
                return headers;
            }

            @Override
            public void addHeader(String key, String value) {
                headers.put(key, value);
            }
        };
    }
}
